package com.chengyu.stock.service.impl;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.util.StringUtils;
import com.chengyu.stock.mapper.*;
import com.chengyu.stock.pojo.domain.*;
import com.chengyu.stock.pojo.entity.StockBusiness;
import com.chengyu.stock.pojo.vo.StockInfoConfig;
import com.chengyu.stock.service.StockService;
import com.chengyu.stock.utils.DateTimeUtil;
import com.chengyu.stock.vo.resp.PageResult;
import com.chengyu.stock.vo.resp.R;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.apache.commons.collections4.CollectionUtils;
import org.joda.time.DateTime;
import org.joda.time.Period;
import org.joda.time.format.DateTimeFormat;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletResponse;
import javax.swing.*;
import java.io.IOException;
import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class StockServiceImpl implements StockService {
    @Autowired
    private Cache<String, Object> caffeineCache;
    @Autowired
    private StockMarketIndexInfoMapper stockMarketIndexInfoMapper;
    @Autowired
    private StockInfoConfig stockInfoConfig;
    @Autowired
    private StockBlockRtInfoMapper stockBlockRtInfoMapper;
    @Autowired
    private StockRtInfoMapper stockRtInfoMapper;

    @Autowired
    private StockOuterMarketIndexInfoMapper stockOuterMarketIndexInfoMapper;

    @Autowired
    private StockBusinessMapper stockBusinessMapper;

    //获取国内大盘的最新交易数据
    @Override
    public R<List<InnerMarketDomain>> getNewestInnerMarketInfos() {
        //先从本地缓存中拿数据，如果不存在 执行补偿策略 并存入缓存
        R<List<InnerMarketDomain>> data = (R<List<InnerMarketDomain>>) caffeineCache.get("innerMarketInfosKey", KeyStroke -> {
            //补偿策略
            //获取股票的最新的交易时间点
            DateTime curDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
            //TODO mock测试数据，后期数据通过第三方接口动态获取实时数据 可删除
            curDateTime=DateTime.parse("2022-01-02 14:49:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
            Date curDate = curDateTime.toDate();
            //获取国内大盘的编码集合
            List<String> innerCodes = stockInfoConfig.getInner();
            //调用mapper相关接口获取数据
            List<InnerMarketDomain> infos = stockMarketIndexInfoMapper.getInnerMarketInfos(curDate, innerCodes);
            //返回响应
            return R.ok(infos);
        });
        return data;
    }

    //获取板块交易数据
    @Override
    public R<List<StockBlockDomain>> getNewestSectorMarketInfos() {
        //获取股票的最新的交易时间点
        DateTime curDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
        //TODO mock测试数据，后期数据通过第三方接口动态获取实时数据 可删除
        curDateTime=DateTime.parse("2022-01-03 09:30:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
        Date curDate = curDateTime.toDate();
        //调用mapper相关接口获取数据
        List<StockBlockDomain> infos = stockBlockRtInfoMapper.getSectorMarketInfos(curDate);
        return R.ok(infos);
    }

    //获取涨幅榜更多数据
    @Override
    public R<PageResult<StockUpdownDomain>> getNewestStockPageInfo(Integer page, Integer pageSize) {
        //设置分页参数
        PageHelper.startPage(page, pageSize);
        //获取最新交易时间点
        Date curDate = DateTimeUtil.getLastDate4Stock(DateTime.now()).toDate();
        //todo 临时时间
        curDate = DateTime.parse("2022-01-06 14:49:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        //查询出数据
        List<StockUpdownDomain> infos = stockRtInfoMapper.getStockPageInfo(curDate);
        //构建pageInfo对象将查询出的数据存入对象中
        PageInfo<StockUpdownDomain> pageInfo = new PageInfo<>(infos);
        //构建pageResult对象 将pageInfo对象中需要的数据取出
        PageResult<StockUpdownDomain> pageResult = new PageResult<>(pageInfo);
        //返回响应
        return R.ok(pageResult);
    }

    //获取涨幅榜数据
    @Override
    public R<List<StockUpdownDomain>> getNewestStockIncreaseInfo() {
        //获取最新交易时间点
        Date curDate = DateTimeUtil.getLastDate4Stock(DateTime.now()).toDate();
        //todo 临时时间
        curDate = DateTime.parse("2022-01-06 14:49:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        //查询出数据
        List<StockUpdownDomain> infos = stockRtInfoMapper.getStockPageInfo(curDate);
        //获取前4个值
        List<StockUpdownDomain> newInfos = infos.subList(0, 4);
        //返回响应
        return R.ok(newInfos);
    }

    //获取涨跌停数据
    @Override
    public R<Map> getStockUpdownCount() {
        //1.获取最新的交易时间范围 openTime  curTime
        //1.1 获取最新股票交易时间点
        DateTime curDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
        //TODO
        curDateTime = DateTime.parse("2022-01-07 14:35:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
        Date endDate = curDateTime.toDate();
        //1.2 获取最新交易时间对应的开盘时间
        DateTime openDate = DateTimeUtil.getOpenDate(curDateTime);
        Date openTime = openDate.toDate();
        //2.查询涨停数据
        //约定mapper中flag入参： 1-》涨停数据 0：跌停
        List<Map> upCounts = stockRtInfoMapper.getStockUpdownCount(openTime, endDate, 1);
        //3.查询跌停数据
        List<Map> dwCounts = stockRtInfoMapper.getStockUpdownCount(openTime, endDate, 0);
        //组装数据
        HashMap<String, List> mapInfo = new HashMap<>();
        mapInfo.put("upList", upCounts);
        mapInfo.put("downList", dwCounts);
        //返回响应
        return R.ok(mapInfo);
    }

    //将指定页的股票数据导出到excel表下
    @Override
    public void stockExport(HttpServletResponse response, Integer page, Integer pageSize) {
        //设置分页参数
        PageHelper.startPage(page, pageSize);
        //获取最新交易时间点
        Date curDate = DateTimeUtil.getLastDate4Stock(DateTime.now()).toDate();
        //todo 临时时间
        curDate = DateTime.parse("2022-01-06 14:49:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        //查询出数据
        List<StockUpdownDomain> infos = stockRtInfoMapper.getStockPageInfo(curDate);

        // 这里注意 有同学反应使用swagger 会导致各种问题，请直接用浏览器或者用postman
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
        try {
            String fileName = URLEncoder.encode("股票指数", "UTF-8");
            response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
            EasyExcel.write(response.getOutputStream(), StockUpdownDomain.class).sheet("股票涨幅数据").doWrite(infos);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    //昨日今日交易量对比(成交量为沪市和深市成交量之和)
    @Override
    public R<Map<String, List>> getStockMarketTradeCompare() {
        //获取最新交易时间点
        DateTime curDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
        //todo 临时时间
        curDateTime = DateTime.parse("2022-01-03 14:59:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
        //获取最新交易时间点对应的开盘时间
        DateTime openDateTime = DateTimeUtil.getOpenDate(curDateTime);
        //获取昨天交易时间点
        DateTime preDateTime = DateTimeUtil.getPreviousTradingDay(curDateTime);
        //获取昨日开盘时间点
        DateTime preOpenDateTime = DateTimeUtil.getOpenDate(preDateTime);
        //转为date
        Date curDate = curDateTime.toDate();
        Date openDate = openDateTime.toDate();
        Date preDate = preDateTime.toDate();
        Date preOpenDate = preOpenDateTime.toDate();
        //获取国内大盘的编码集合
        List<String> innerCodes = stockInfoConfig.getInner();
        //获取当时成交量信息
        List<Map> amtList = stockMarketIndexInfoMapper.getStockMarketTradeAmount(innerCodes, curDate, openDate);
        //获取上一天成交量信息
        List<Map> yesAmtList = stockMarketIndexInfoMapper.getStockMarketTradeAmount(innerCodes, preDate, preOpenDate);
        //组装响应数据
        Map<String, List> infos = new HashMap<>();
        infos.put("amtList", amtList);
        infos.put("yesAmtList", yesAmtList);
        return R.ok(infos);
    }

    //个股分时涨跌幅度统计功能
    @Override
    public R<Map<String, Object>> getLastDate4Stock() {
        //获取最新交易时间点
        DateTime curDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
        //todo 临时时间
        curDateTime = DateTime.parse("2022-01-06 14:25:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
        Date date = curDateTime.toDate();

        //获取数据
        List<Map> data = stockRtInfoMapper.getLastDate4Stock(date);
        //补齐不存在的字段
        List<String> upDownRange = stockInfoConfig.getUpDownRange();
        //收集结果
        ArrayList<Map> all = new ArrayList<Map>();
        for (String section : upDownRange) {
            Map<String, Object> tmp = null;
            //去data集合中找有没有这个标题的对象
            for (Map map : data) {
                if (map.containsValue(section)) {
                    tmp = map;
                    //存在 循环结束
                    break;
                }
            }
            //如果没有值 创建一个空值
            if (tmp == null) {
                tmp = new HashMap<>();
                tmp.put("count", 0);
                tmp.put("title", section);
            }
            //将tmp添加到all
            all.add(tmp);
        }
        //组装数据
        HashMap<String, Object> Info = new HashMap<>();
        //获取指定日期格式的字符串
        String dateStr = new DateTime(date).toString(DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
        Info.put("time", dateStr);
        Info.put("infos", all);
        return R.ok(Info);
    }

    //股票分时K线图
    @Override
    public R<List<Stock4MinuteDomain>> getStockScreenTimeSharing(String code) {
        //获取最新交易时间点
        DateTime curDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
        //todo 临时时间
        curDateTime = DateTime.parse("2022-01-06 14:49:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
        //获取最新交易时间点对应的开盘时间
        DateTime openDateTime = DateTimeUtil.getOpenDate(curDateTime);
        //转为date
        Date curDate = curDateTime.toDate();
        Date openDate = openDateTime.toDate();

        List<Stock4MinuteDomain> info = stockRtInfoMapper.getStockScreenTimeSharing(code, curDate, openDate);
        //判断非空处理
        if (CollectionUtils.isEmpty(info)) {
            info = new ArrayList<>();
        }
        //3.返回响应数据
        return R.ok(info);
    }

    //股票日K线图
    @Override
    public R<List<Stock4EvrDayDomain>> getStockScreenEvrDaySharing(String code) {
        //1.1 先获取最新交易时间点
        DateTime endDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
        Date endDate = endDateTime.toDate();
        //1.2 获取开始日期时间 约定：统计最近4个月的股票每天的数据
        DateTime startDateTime = endDateTime.minusMonths(4);
        //mock data
        startDateTime=DateTime.parse("2022-01-06 14:49:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
        Date startDate = startDateTime.toDate();
        //2.mapper查询
        List<Stock4EvrDayDomain> infos= stockRtInfoMapper.getStockScreenEvrDaySharing(code ,startDate,endDate);
        //3.响应
        return R.ok(infos);
    }

    //获取外盘数据StockOuterMarketDomain
    @Override
    public R<List<StockOuterMarketDomain>> getStockOuterMarketInfos() {
        //获取外盘编码集合
        List<String> outerCodes = stockInfoConfig.getOuter();
        List<StockOuterMarketDomain> infos = stockOuterMarketIndexInfoMapper.getStockOuterMarketInfos(outerCodes);

        return R.ok(infos);
    }

    //股票Code联想推荐功能
    @Override
    public R<List<Map>> searchStock(String searchStr) {
        if (StringUtils.isBlank(searchStr)) {
            return R.error("暂无数据");
        }
        List<Map> maps= stockBusinessMapper.searchStockLike(searchStr);
        if (CollectionUtils.isEmpty(maps)) {
            return R.error("暂无数据");
        }
        return R.ok(maps);

    }

    //个股描述功能
    @Override
    public R<StockBusinessDomain> getStockDescribe(String code) {
        StockBusinessDomain info = stockBusinessMapper.getStockDescribe(code);
        if (info == null){
            return R.error("暂无数据");
        }
        return R.ok(info);
    }

    @Override
    public R<List<Stock4EvrWeekDomain>> getWeekKLinData(String code) {
        //指定默认日期范围下的数据
        DateTime curDateTime = DateTime.now();
        //截止时间
        Date endTime = curDateTime.toDate();
        endTime=DateTime.parse("2022-05-22 09:30:00",DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        //开始时间
        Date startTime = curDateTime.minusWeeks(10).toDate();
        startTime=DateTime.parse("2021-01-01 09:30:00",DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        //根据指定日期范围查询周K线数据
//        List<Stock4EvrWeekDomain> infos=stockRtInfoMapper.getWeekLineData(stockCode,startTime,endTime);
        List<Stock4EvrWeekDomain> infos=stockRtInfoMapper.getHalfWeekLineData(code,startTime,endTime);
        //获取每周的开盘时间点和收盘时间点的集合
        //每周开盘时间点
        List<Date> everyOpenTime = infos.stream().map(info -> info.getMiTime()).collect(Collectors.toList());
        //每周收盘盘时间点 最大开盘时间点对应的股票信息就包含了开盘价和收盘价信息
        List<Date> everyCloseTime = infos.stream().map(info -> info.getMxTime()).collect(Collectors.toList());
//        Map<String,List<Date>> map=new HashMap<>();
//        map.put("closes",new ArrayList<>());
//        map.put("opens",new ArrayList<>());
//        infos.stream().forEach(info -> {
//            map.get("opens").add(info.getMiTime());
//            map.get("closes").add(info.getMxTime());
//        });
//        //分别查询指定股票在指定时间点下的数据集合
        List<BigDecimal> openPrices=stockRtInfoMapper.getStockInfoByCodeAndTimes(code,everyOpenTime);
        List<BigDecimal> closePrices=stockRtInfoMapper.getStockInfoByCodeAndTimes(code,everyCloseTime);
        //将获取的开盘价格和收盘价格合并到基础查询结果中
        for (int i = 0; i < infos.size(); i++) {
            infos.get(i).setOpenPrice(openPrices.get(i));
            infos.get(i).setClosePrice(closePrices.get(i));
        }
        return R.ok(infos);

    }


}
